(function (root, factory) {

  'use strict';

  if (typeof define === 'function' && define.amd) {
    // AMD. Register as an anonymous module.
    /*define([], function () {
      return (root.returnExportsGlobal = factory());
    });*/
    define("markdownItRules", [], factory);
  } else if (typeof exports === 'object') {
    // Node. Does not work with strict CommonJS, but
    // only CommonJS-like enviroments that support module.exports,
    // like Node.
    // module.exports = factory();
    exports["markdownItRules"] = factory();
  } else {
    // Browser globals
    // root.returnExportsGlobal = factory();
    root["markdownItRules"] = factory();
  }

}(this, function () {
// UMD Definition above, do not remove this line

// To get to know more about the Universal Module Definition
// visit: https://github.com/umdjs/umd
  'use strict';
  /// 覆盖默认的 blockquote 渲染策略
  function overWriteBlockquoteRule(md) {
    md.renderer.rules.blockquote_open = function (tokens, idx, options, env, slf) {
      const token = tokens[idx];
      token.attrJoin('class', 'markdown__blockquote');
      return `<blockquote ${slf.renderAttrs(token)}>`;
      // return `<blockquote class="markdown__blockquote">`;
    };
    md.renderer.rules.blockquote_close = function (tokens, idx, options, env, slf) {
      return `</blockquote>`;
    };
  }


/// 覆盖默认的 fence 渲染策略
  const wrap = function (render) {
    return function () {
      return render.apply(this, arguments)
        .replace('<pre>', ' <pre class="markdown__fences">')
        .replace('<code class="', '<code class="hljs ')
        .replace('<code>', '<code class="hljs">')
    };
  };

// 代码添加行号
  const CODE = /(<code class="[^>]*>|<code[^>]*>)/g;
  function prewrapper(md) {
    const fence = md.renderer.rules.fence;
    const codeBlock = md.renderer.rules.code_block;

    const wrapperFn = function(rule,...args) {
      const [tokens, idx] = args;
      const token = tokens[idx];
      const rawCode = rule(...args);
      var modifCode = rawCode.replace(CODE, `<code  class="language-${token.info.trim()} hljs"><!--codebegin-->`);
      modifCode = modifCode.replace(/(<\/code>)/g, `<!--codeend--></code>`);
      return `<div class="code-wrapper markdown__fences">${modifCode}</div>`
    }
    md.renderer.rules.fence = (...args) => {
      return wrapperFn(fence, ...args)
    };

    md.renderer.rules.code_block = (...args) => {
      return wrapperFn(codeBlock, ...args)
    }
  }

  function lineNumbers(md) {
    const fence = md.renderer.rules.fence;
    const codeBlock = md.renderer.rules.code_block;
    const updateLineNumber = function(rule, ...args) {
      const rawCode = rule(...args);

      const code = rawCode.slice(
        rawCode.indexOf('<!--codebegin-->') + '<!--codebegin-->'.length,
        rawCode.indexOf('<!--codeend-->')
      );

      var lines = code.split('\n');
      if (lines[lines.length - 1] === '') {
        lines.splice(-1, 1);
      }
      if (lines.length > 0) {
        lines = lines.map(line => '<span class="code-line">' + line + '</span>');
      }
      return rawCode.replace(`<!--codebegin-->${code}<!--codeend-->`, lines.join('\n'));
    };
    md.renderer.rules.fence = (...args) => {
      return updateLineNumber(fence, ...args);
    }
    md.renderer.rules.code_block = (...args) => {
      return updateLineNumber(codeBlock, ...args)
    }
  }


  function overWriteFenceRule(md) {
    const defaultRender = md.renderer.rules.fence;
    md.renderer.rules.fence = (tokens, idx, options, env, slf) => {
      const token = tokens[idx];
      const info = token.info ? md.utils.unescapeAll(token.info).trim() : '';
      const highlighted = md.utils.escapeHtml(token.content);
      if (info === 'sequence') {
        return `<diagram-sequence data="${highlighted}" theme="simple">${highlighted}</diagram-sequence>`
      } else if (info === 'flow') {
        return `<diagram-flow data="${highlighted}">${highlighted}</diagram-flow>`
      } else if (info === 'mermaid') {
        return `<diagram-mermaid data="${highlighted}">${highlighted}</diagram-mermaid>`
      }
      token.attrJoin('class', 'hljs');
      return defaultRender(tokens, idx, options, env, slf);
      // token.attrJoin('class', 'hljs');
      // let result = defaultRender(tokens, idx, options, env, slf);
      // return result.replace('<pre v-pre>', '<pre class="markdown__fences">')
    };
  }

/// 覆盖默认的 heading 渲染策略
  function overWriteHeadingRule(md) {
    md.renderer.rules.heading_open = function (tokens, idx, options, env, slf) {
      const token = tokens[idx];
      token.attrJoin('class', `markdown__heading--${token.tag}`);
      return `<${token.tag} ${slf.renderAttrs(token)}>`;
      // return `<${token.tag} class="markdown__heading--${token.tag}">`
    };

    md.renderer.rules.heading_close= function (tokens, idx, options, env, slf) {
      const token = tokens[idx];
      return `</${token.tag}>`
    };
  }

  /// 覆盖默认的 link 渲染策略
  function overWriteLinkRule(md) {
    md.renderer.rules.link_open = function (tokens, idx, options, env, slf) {
      const token = tokens[idx];
      token.attrJoin('class', 'markdown__link');

      const href = token.attrGet('href');
      let getAbsolutePath = getAbsolutePathFn(md);
      if (getAbsolutePath) {
        token.attrSet('href', getAbsolutePath(decodeURIComponent(href)));
      }
      return `<a ${slf.renderAttrs(token)}>`;
    };
    md.renderer.rules.link_close = function (tokens, idx, options, env, slf) {
      return `</a>`;
    };
  }

/// 覆盖默认的 list 渲染策略
  function overWriteListRule(md) {
    md.renderer.rules.bullet_list_open = function (tokens, idx, options, env, slf) {
      const token = tokens[idx];
      token.attrJoin('class', 'markdown__list markdown__list-bullet');
      return `<ul ${slf.renderAttrs(token)}>`;
    };
    md.renderer.rules.bullet_list_close = function () {
      return '</ul>';
    };


    md.renderer.rules.ordered_list_open = function () {
      return '<ol class="markdown__list markdown__list-ordered">';
    };
    md.renderer.rules.ordered_list_close = function () {
      return '</ol>';
    };

    md.renderer.rules.list_item_open = function (tokens, idx, options, env, slf) {
      const token = tokens[idx];
      token.attrJoin('class', 'markdown__list-item')
      return `<li ${slf.renderAttrs(token)}>`;
    };
    md.renderer.rules.list_item_close = function (tokens, idx, options, env, slf) {
      const token = tokens[idx];
      token.attrJoin('class', 'markdown__list-item')
      return `</li>`;
    };
  }

/// 覆盖默认的 paragraph 渲染策略
  function overWriteParagraphRule(md) {
    md.renderer.rules.paragraph_open = function (tokens, idx, options, env, slf) {
      const token = tokens[idx];
      token.attrJoin('class', 'markdown__paragraph');
      return `<${token.tag} ${slf.renderAttrs(token)}>`;
      // return `<${token.tag} class="markdown__paragraph">`
    };
    md.renderer.rules.paragraph_close = function (tokens, idx, options, env, slf) {
      const token = tokens[idx];
      return `</${token.tag}>`
    };
  }

/// 覆盖默认的 table 渲染策略
  function overWriteTableRule(md) {
    md.renderer.rules.table_open = function (tokens, idx, options, env, slf) {
      const token = tokens[idx];
      token.attrJoin('class', 'markdown__table');
      return `<figure class="markdown__figure"><table ${slf.renderAttrs(token)}>`;
    };
    md.renderer.rules.table_close = function () {
      return '</table></figure>';
    };
  }

  /**
   * override default parser rules by adding v-pre attribute on 'code' and 'pre' tags
   * @param {Array<string>} rules rules to override
   */
  /*const addVuePreviewAttr = function(str) {
    return str.replace(/(<pre|<code)/g, '$1');
  };

  function overrideParserRules(parser, rules) {
    if (parser && parser.renderer && parser.renderer.rules) {
      var parserRules = parser.renderer.rules;
      rules.forEach(function(rule) {
        if (parserRules && parserRules[rule]) {
          var defaultRule = parserRules[rule];
          parserRules[rule] = function() {
            return addVuePreviewAttr(defaultRule.apply(this, arguments));
          };
        }
      });
    }
  }*/

  function getAbsolutePathFn(md) {
    const assetsPublicPath =  md.options.assetsPublicPath;
    let pathFn = function(relativePath) { return relativePath};
    if (!assetsPublicPath) return null;
    if (Object.prototype.toString.call(assetsPublicPath) === "[object String]") {
      pathFn = function (relativePath) {
        return `${assetsPublicPath}${relativePath}`
      }
    } else if (Object.prototype.toString.call(assetsPublicPath) === "[object Function]"){
      pathFn = assetsPublicPath
    }

    return pathFn
  }

  function overWriteImageRule(md) {
    const defaultRender = md.renderer.rules.image;
    // const assetsPublicPath =  md.options.assetsPublicPath;
    let getAbsolutePath = getAbsolutePathFn(md);
    md.renderer.rules.image = function(tokens, idx, options, env, slf) {
      var token = tokens[idx];
      if (getAbsolutePath) {
        let result = getAbsolutePath(token.attrs[token.attrIndex("src")][1]);
        if (result) token.attrs[token.attrIndex("src")][1] = result
      }
      /*if (!assetsPublicPath) return defaultRender(tokens, idx, options, env, slf);
      if (Object.prototype.toString.call(assetsPublicPath) === "[object String]") {
        getAbsolutePath = function (relativePath) {
          return `${assetsPublicPath}${relativePath}`
        }
      } else if (Object.prototype.toString.call(assetsPublicPath) === "[object Function]"){
        getAbsolutePath = assetsPublicPath
      }
      if (getAbsolutePath) {
        let result = getAbsolutePath(token.attrs[token.attrIndex("src")][1]);
        if (result) token.attrs[token.attrIndex("src")][1] = result
      }*/
      return defaultRender(tokens, idx, options, env, slf)
    }
  }

  var attr_name = "[a-zA-Z_:][a-zA-Z0-9:._-]*";
  var unquoted = "[^\"'=<>`\\x00-\\x20]+";
  var single_quoted = "'[^']*'";
  var double_quoted = '"[^"]*"';
  var attr_value = "(?:" + unquoted + "|" + single_quoted + "|" + double_quoted + ")";
  var attribute = "(?:\\s+" + attr_name + "(?:\\s*=\\s*" + attr_value + ")?)";
  function overWriteHtmlInlineRule(md) {
    md.renderer.rules.html_inline = function (tokens, idx , options, env ) {
      const token = tokens[idx];
      /*var assetsPublicPath = md.options.assetsPublicPath;
      var getAbsolutePath;*/
      const attributes = token.content.matchAll(attribute);
      // if (!assetsPublicPath) return tokens[idx].content;
     /* if (Object.prototype.toString.call(assetsPublicPath) === "[object String]") {
        getAbsolutePath = function (relativePath) {
          return `${assetsPublicPath}${relativePath}`
        }
      } else if (Object.prototype.toString.call(assetsPublicPath) === "[object Function]"){
        getAbsolutePath = assetsPublicPath
      }*/
     let getAbsolutePath = getAbsolutePathFn(md);
     if (!getAbsolutePath) return tokens[idx].content;
      for (const match of attributes) {
        let attrName = match[0].split('=')[0] || '';
        let attrValue = match[0].split('=')[1] || '""';

        if (attrName.trim() === 'src' || attrName.trim() === 'href') {
          let result = getAbsolutePath(JSON.parse(attrValue));
          if (result) {
            token.content = token.content.replace(match[0].trim(), `href="${result}" class="markdown__link"`);
          }
        }
      }
      /*if (LINK.test(token.content) && fn) {
        const HREF = /href="(.*?)"/g;
        let attrsHref = token.content.match(HREF);
        attrsHref.forEach(elem=>{
          var hrefValue = elem.split('=')[1];
          if (hrefValue) {
            hrefValue = JSON.parse(hrefValue);
            let result = fn(hrefValue);
            if (result) {
              token.content = token.content.replace(elem, `href="${result}" class="markdown__link"`);
            }
          }
        })
      }*/
      return tokens[idx].content;
    };
  }

  function overWriteAllRules(md) {
    // overrideParserRules(md, ['code_inline', 'code_block', 'fence']);
    overWriteBlockquoteRule(md);
    overWriteFenceRule(md);
    prewrapper(md);
    lineNumbers(md);
    overWriteHeadingRule(md);
    overWriteLinkRule(md);
    overWriteListRule(md);
    overWriteParagraphRule(md);
    overWriteTableRule(md);
    overWriteImageRule(md);
    overWriteHtmlInlineRule(md);
  }

  return {
    // overrideParserRules,
    overWriteBlockquoteRule,
    overWriteAllRules,
    overWriteHeadingRule,
    overWriteLinkRule,
    overWriteListRule,
    overWriteParagraphRule,
    overWriteTableRule,
    overWriteImageRule,
  }
}));
